package com.heima.comment.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.apis.comment.UserFeignClient;
import com.heima.comment.dtos.CommentLikeDto;
import com.heima.comment.dtos.CommentListDto;
import com.heima.comment.pojos.ApCommentLike;
import com.heima.comment.pojos.ApCommentVo;
import com.heima.comment.service.CommentService;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.model.comment.dto.CommentSaveDto;
import com.heima.comment.pojos.ApComment;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.pojos.ApUser;
import com.heima.utils.thread.ApTheadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.constraints.Min;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author shitou
 * @className CommentServiceImpl
 * @description: TODO
 */
@Service
@Slf4j
public class CommentServiceImpl implements CommentService {

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserFeignClient userFeignClient;

    @Resource
    private GreenTextScan textScan;

    /**
     * 发表评论方法
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult saveComment(CommentSaveDto dto) {
        log.info("发表评论方法已经执行");
        // 校验参数
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        Long articleId = dto.getArticleId();
        if (articleId == null || articleId < 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 用户未登录
        ApUser apUser = ApTheadLocalUtil.getApUser();
        if (apUser == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        Integer userId = apUser.getId();
        // 游客需要进行登录
        if (userId == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        // 评论不能为空评   论字数不超过140
        if (StringUtils.isBlank(dto.getContent()) || dto.getContent().length() > 140) {
            return ResponseResult.errorResult(AppHttpCodeEnum.ERROR_COMMENT);
        }

        // redis 缓存用户信息，有的话不需要缓存，用户信息直接返回，没有则远程调用获取用户信息, redis数据插入
        String loginUser = stringRedisTemplate.opsForValue().get("LOGIN_USER_" + userId);
        if (loginUser == null) {
            // 状态码200
            ResponseResult<ApUser> responseResult = userFeignClient.findUserById(userId);
            if (responseResult.getCode() != 200) {
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
            }
            // 用户不存在
            apUser = responseResult.getData();
            if (apUser == null) {
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
            }

            // 用户信息存在存入redis 存活2小时
            stringRedisTemplate.opsForValue().set("LOGIN_USER_" + userId, JSON.toJSONString(apUser), 2, TimeUnit.HOURS);
        } else {
            // 取出数据
            apUser = JSON.parseObject(loginUser, ApUser.class);
        }

        // 审核评论信息
        boolean flag = scanTextFilter(dto.getContent());
        // 评论内容违规
        log.info("flag:{}", flag);
        if (!flag) {
            return ResponseResult.errorResult(AppHttpCodeEnum.ERROR_COMMENT_VIOLATION);
        }

        // 数据封装进行mongo数据插入
        ApComment apComment = new ApComment();
        apComment.setContent(dto.getContent());
        apComment.setImage(apUser.getImage());
        apComment.setNickName(apUser.getName());
        apComment.setTargetId(dto.getArticleId());
        apComment.setUpdatedTime(new Date());
        apComment.setCreatedTime(new Date());
        apComment.setUserId(apUser.getId());
        apComment.setLikes(0);
        apComment.setReply(0);
        mongoTemplate.insert(apComment);

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 评论点赞
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult like(CommentLikeDto dto) {
        log.info("评论点赞方法已经执行");
        // 校验参数
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        String commentId = dto.getCommentId();
        if (StringUtils.isBlank(commentId)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 用户未登录
        ApUser apUser = ApTheadLocalUtil.getApUser();
        if (apUser == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        Integer userId = apUser.getId();
        // 游客需要进行登录
        if (userId == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        // 评论ID不能为空
        if (dto.getCommentId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 当前评论是否存在
        ApComment apComment = mongoTemplate.findById(commentId, ApComment.class);
        if (apComment == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        String commentLikeUserId = "COMMENT_LIKE_" + userId;
        // 判断当前用户是否点过赞
        Boolean memberFlag = stringRedisTemplate.opsForSet().isMember(commentLikeUserId, commentId);
        if (memberFlag == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }
        // 点赞行为
        Short operation = dto.getOperation();

        // 用户点过赞，并再次执行点赞操作，直接返回，提示已经点过赞
        if (memberFlag && operation == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.COMMENT_ALREADY_LIKE);
        }
        // 用户点过赞，执行点赞取消操作
        if (memberFlag && operation == 1) {
            // 删除redis 与 mongo中点赞集合
            stringRedisTemplate.opsForSet().remove(commentLikeUserId, commentId);
            mongoTemplate.remove(Query.query(Criteria.where("userId").is(userId).and("targetId").is(commentId)));
            // 修改评论集合中点赞数量
            Query query = Query.query(Criteria.where("id").is(commentId).and("likes").gt(0));
            UpdateDefinition update = new Update().inc("likes", -1);
            mongoTemplate.updateFirst(query, update, ApComment.class);
        }
        // 用户没有点过赞，进行点赞操作
        if (!memberFlag && operation == 0) {
            // 点赞集合数量添加 + 1
            Query query = Query.query(Criteria.where("id").is(commentId));
            UpdateDefinition update = new Update().inc("likes", 1);
            mongoTemplate.updateFirst(query, update, ApComment.class);
            // 增加点赞集合关系
            ApCommentLike apCommentLike = new ApCommentLike();
            apCommentLike.setCreatedTime(new Date());
            apCommentLike.setUserId(userId);
            apCommentLike.setTargetId(commentId);
            mongoTemplate.insert(apCommentLike);
            // redis 增加点赞信息
            stringRedisTemplate.opsForSet().add(commentLikeUserId, commentId);
        }
        // 返回数据
        apComment = mongoTemplate.findById(dto.getCommentId(), ApComment.class);
        if (apComment == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        Map<String, Integer> map = new HashMap<>();
        map.put("likes", apComment.getLikes());

        return ResponseResult.okResult(map);
    }

    /**
     * 加载评论列表
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findByArticleId(CommentListDto dto) {
        log.info("加载评论列表");
        // 参数校验
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        Integer size = dto.getIndex();
        if (size == null || size <= 0) {
            dto.setIndex(10);
        }
        Long articleId = dto.getArticleId();
        if (articleId == null || articleId <= 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        dto.setIndex(Math.min(size, 50));

        // 根据文章id去查询mongo的评论表，并进行分页和排序展示
        Query query = Query.query(Criteria.where("targetId").is(articleId).and("createdTime").lt(dto.getMinDate())).with(Sort.by(Sort.Direction.DESC, "createdTime")).limit(size);
        List<ApComment> apCommentList = mongoTemplate.find(query, ApComment.class);
        if (apCommentList == null && apCommentList.size() < 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        // 如果此刻是游客登录的话，则直接返回评论列表
        ApUser apUser = ApTheadLocalUtil.getApUser();
        if (apUser.getId() == 0) {
            return ResponseResult.okResult(apCommentList);
        }
        // 否则就是用户登录，加载数据的时候，要判断当前用户点赞了哪些评论，并给予标识
        String key = "COMMENT_LIKE_" + apUser.getId();
        Set<String> like_ids = stringRedisTemplate.opsForSet().members(key);
        // 如果没有查询到数据,则直接返回评论列表
        if (like_ids == null || like_ids.size() == 0) {
            return ResponseResult.okResult(apCommentList);
        }
        // 否则，就是存在点赞数据，则添加标记
        List<ApCommentVo> resultList = apCommentList.stream().map(comment -> {
            ApCommentVo vo = new ApCommentVo();
            try {
                BeanUtils.copyProperties(comment, vo);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            if (like_ids.contains(comment.getId())) {
                vo.setOperation((short) 0);
            }
            return vo;

        }).collect(Collectors.toList());
        return ResponseResult.okResult(resultList);
    }

    /**
     * 评论内容审核方法
     *
     * @param content
     * @return
     */
    private boolean scanTextFilter(String content) {
        log.info("评论内容审核方法已执行");
        boolean flag = false;
        try {
            Map map = textScan.greeTextScan(content);
            // 审核通过
            if (map != null && map.get("suggestion").equals("pass")) {
                flag = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }
}
